/**
 * @file   NewickTreeFactory.cpp
 * @author Scanagatta Mauro - Alberto Franco
 *
 * 
 */
#include "NewickTreeFactory.h"
#include <list>
#include <sstream>
#include <iomanip>
#include <cstdlib>

#include <iostream>

namespace Biopool
{

////////////////////////////////////////////////////////////////////////////////
/// SERIALIZATION

/**
 * Serialize the given tree into a string.
 * @param tree The tree to serialize.
 * @return The string with the serialized tree.
 */
std::string NewickTreeFactory::serialize(NewickTree& tree)
{
    return treeDescend(tree.root);
}

/// Recursive procedure to descend into the tree and print
std::string NewickTreeFactory::treeDescend(NewickTree::Node* node)
{
    if (node->isLeaf())
    {
        std::stringstream ss;
        ss.precision(5);
        ss << serializeSet(node->objectRef) << ":"  << node->branchLength << " ";
        return ss.str();
    }
    else
    {
        std::string left  = treeDescend(node->leftChild);
        std::string right = treeDescend(node->rightChild);
        std::stringstream ss;
        ss << "(" << left << ", " << right << "):" << node->branchLength << " ";
        return ss.str();
    }
}

/// Serialize a set and return the string of it serialized
std::string NewickTreeFactory::serializeSet(intSet* set)
{
    intSet::iterator it = set->begin();
    std::stringstream output;
    output << *it; // Usually one element per leaf
    return output.str();
}

////////////////////////////////////////////////////////////////////////////////
/// DESERIALIZATION

/**
 * Deserialize the tree from a given string
 * @param treeString The string to deserialize.
 * @return The tree rebuilt from the string
 */ 
NewickTree* NewickTreeFactory::deserialize(std::string treeString) 
{
    std::string::iterator it = treeString.begin();
    return new NewickTree(descendDeserialize(it, treeString));
}

/// Recursvie function to descend the tree and read it from the string.
NewickTree::Node* NewickTreeFactory::descendDeserialize(std::string::iterator& it, 
                                                        std::string&           str)
{
    if (it != str.end())
    {
        // Variable declarations
        NewickTree::Node* node, *left, *right;
        float length;
        intSet* mySet = new intSet();
        
        while (*it == ' ') { it++; } // Skip spaces
        if (*it == '(') // Internal node
        {
            it++; // Next char
            left = descendDeserialize(it, str);
            while (*it == ' ') { it++; } // Skip spaces
            if (*it == ',') 
            {
                it++;
                right = descendDeserialize(it, str);
            }
            else
                throw "Error! no comma found";
            while (*it == ' ') { it++; } // Skip spaces
            if (*it == ')') it++;
            else throw "Error, no close parenthesis found!";
            if (*it == ':')
            {
                it++;
                std::string number;
                while (*it != ' ')  
                {
                    number.push_back(*it);
                    it++;
                }
                length = atof(number.c_str());
            }
            else
                throw "Error! No semicolon found!";
            intSet::iterator it;
            for (it = left->objectRef->begin(); it != left->objectRef->end(); it++)
                mySet->insert(*it);
            while (*it == ' ') { it++; } // Skip spaces
            node = new NewickTree::Node(length, mySet, right, left);
        }
        else // Node is a leaf
        {
            std::string number;
            // Read sequence number
            while (*it != ':') 
            {
                number.push_back(*it);
                it++;
            }
            intSet* mySet = new intSet();
            mySet->insert(atoi(number.c_str()));
            it++; number = "";
            // Read distance
            while (*it != ' ')
            {
                number.push_back(*it);
                it++;
            }
            // Return the leaf
            return new NewickTree::Node(atof(number.c_str()), mySet, 0, 0);
        }
        return node;
    }
    else 
    {
        throw "Error! Not terminated before end reached";
    }
}

} // namespace Biopool

